// -*- c-basic-offset: 2 default-tab-width: 2 indent-tabs-mode: t -*-
// vim: autoindent tabstop=2 noexpandtab shiftwidth=2 softtabstop=2
#include "Any.h"
#include "Array.h"
#include "util.h"
#include "Arithmetic.h"
#include "Convert.h"
#include <cmath>

#define logical unsigned int

#define defBinOp(name, op, type) \
	struct name ## impl { \
		double operator() (double a, double b) { return op(a, b) ; } \
		double operator() (double a, int b) { return op(a, b) ; } \
		double operator() (double a, logical b) { return op(a, ((double)b)) ; } \
		int operator() (int a, int b) { return op(a, (b)) ; } \
		int operator() (int a, logical b) { return op(a, ((double)b)) ; } \
		logical operator() (logical a, logical b) { return op(a, ((double)b)) ; } \
	};\
Any* name(Any *l_expr, Any *r_expr){\
	return type < name ## impl >(l_expr, r_expr); \
}


static inline R::r_bool isInteger(double x){
	return x == (double)(long)x;
}

R::r_double fast_pow(double x, long y){
	// never fast !
	 return 1;
}

namespace R {
	//namespace {
#define dispatchError dispatchErrorArithmetic
	void dispatchErrorArithmetic(Any *x, Any *y){
		rfatal("No valid dispatch found for arithmetic operator (" WORD ", " WORD ")", Any::getType(x), Any::getType(y));
	}

	template<typename OP, typename L, typename R>
		Any* arithBinOperatorImpl(L *l_expr, R *r_expr){
			OP op; // THE binary operator
			R_PARAM(l_expr);
			R_PARAM(r_expr);
			R_VAR(L*, res);

			int l_idx = 0, r_idx = 0;
			int l_size = l_expr->length,
					r_size = r_expr->length;
			int size = l_size < r_size ? r_size : l_size;

			res = L::doNew(size);
			mod_iterate(size, l_size, r_size, l_idx, r_idx)
				res->setElement(op(l_expr->getElement(l_idx), r_expr->getElement(r_idx)), i);
			return res;
		}
#include "Arithmetic.inc"

#undef dispatchError
#define dispatchError dispatchErrorRelational
	void dispatchErrorRelational(Any *x, Any *y){
		rfatal("No valid dispatch found for relational operator");
	}

	template<typename OP, typename L, typename R>
		Logical* relBinOperatorImpl(L *l_expr, R *r_expr){
			OP op; // THE binary operator
			R_PARAM(l_expr);
			R_PARAM(r_expr);
			R_VAR(Logical*, res);

			int l_idx = 0, r_idx = 0;
			int l_size = 0, r_size = 0;
			int size = 0;

			l_size = l_expr->length;
			r_size = r_expr->length;

			size = l_size < r_size ? r_size : l_size;

			res = Logical::doNew(size);
			mod_iterate(size, l_idx, r_idx, l_size, r_size)
				res->setElement(op(l_expr->getElement(l_idx), r_expr->getElement(r_idx)), l_idx);
			return res;
		}
#include "Relational.inc"
	//}

#define PLUS(x, y) (x + y)
	defBinOp(add, PLUS, arithBinOperator)
#define SUB(x, y) (x - y)
		defBinOp(sub, SUB, arithBinOperator)
#define MULT(x, y) (x * y)
		defBinOp(mult, MULT, arithBinOperator)
#define DIV(x, y) (x / y)
		defBinOp(div, DIV, arithBinOperator)
		// defBinOp(pow, std::pow, arithBinOperator) // FIXME can't be a std arith op, since it returns at least a double

#define GT(x, y) (x > y)
		defBinOp(gt, GT, relBinOperator)
#define GE(x, y) (x >= y)
		defBinOp(ge, GE, relBinOperator)
#define LT(x, y) (x < y)
		defBinOp(lt, LT, relBinOperator)
#define LE(x, y) (x <= y)
		defBinOp(le, LE, relBinOperator)
#define EQ(x, y) (x == y)
		defBinOp(eq, EQ, relBinOperator)
#define NE(x, y) (x != y)
		defBinOp(ne, NE, relBinOperator)

		template<typename V, typename L, typename R>
		V* powImplementation(L *l_expr, R *r_expr){
			int l_size = static_cast<Array*>(l_expr)->length,
					r_size = static_cast<Array*>(l_expr)->length;
			int res_size = max(l_size, r_size);
			int l_idx, r_idx;

			V* res = V::doNew(res_size);

			mod_iterate(res_size, l_size, r_size, l_idx, r_idx)
				res->setElement(std::pow(
							convert<TYPE(L), TYPE(V)>(l_expr->getElement(l_idx)),
							convert<TYPE(R), TYPE(V)>(r_expr->getElement(r_idx))), i);
			return res;
		}

		Any* pow(Any *l_expr, Any *r_expr){
			R_PARAM(l_expr);
			R_PARAM(r_expr);
			R_VAR(Number*, res);

			int l_type = Any::getType(l_expr), r_type = Any::getType(r_expr);
			int res_type = max(l_type, r_type);

			rerror_on(!Any::isOfType<Number>(l_type), "Power is only valid on numbers");
			rerror_on(!Any::isOfType<Number>(r_type), "Power is only valid on numbers");

			/*if(res_type == Complex::ID){
				nyi_fatal();
			} else {*/
				int lIsReal = Any::isOfType<Double>(l_type);
				int rIsReal = Any::isOfType<Double>(r_type);
#define POWCALL(T1, T2, T3)\
					powImplementation<T1, T2, T3>(static_cast<T2*>(l_expr), static_cast<T3*>(r_expr))
				if(lIsReal && rIsReal)
					res = POWCALL(Double, Double, Double);
				else if(lIsReal)
					res = POWCALL(Double, Double, Int);
				else if(rIsReal)
					res = POWCALL(Double, Int, Double);
				else
					res = POWCALL(Double, Int, Int);
			//}
			return res;
		}
}
